module scvt_mesh_mod

  use flogger
  use fiona
  use container
  use string
  use const_mod
  use delaunay_voronoi_mod
  use sphere_geometry_mod

  implicit none

  private

  public scvt_mesh_output

  type scvt_mesh_type
    integer :: nc = 0 ! Number of cells
    integer :: ne = 0 ! Number of edges
    integer :: nv = 0 ! Number of vertices
    ! Grid locations
    real(8), allocatable, dimension(:  ) :: cell_lon
    real(8), allocatable, dimension(:  ) :: cell_lat
    real(8), allocatable, dimension(:  ) :: cell_x
    real(8), allocatable, dimension(:  ) :: cell_y
    real(8), allocatable, dimension(:  ) :: cell_z
    real(8), allocatable, dimension(:  ) :: edge_lon
    real(8), allocatable, dimension(:  ) :: edge_lat
    real(8), allocatable, dimension(:  ) :: edge_x
    real(8), allocatable, dimension(:  ) :: edge_y
    real(8), allocatable, dimension(:  ) :: edge_z
    real(8), allocatable, dimension(:  ) :: vtx_lon
    real(8), allocatable, dimension(:  ) :: vtx_lat
    real(8), allocatable, dimension(:  ) :: vtx_x
    real(8), allocatable, dimension(:  ) :: vtx_y
    real(8), allocatable, dimension(:  ) :: vtx_z
    ! Geometry
    real(8), allocatable, dimension(:  ) :: edge_arc
    real(8), allocatable, dimension(:  ) :: edge_arc1
    real(8), allocatable, dimension(:  ) :: edge_arc2
    real(8), allocatable, dimension(:  ) :: dual_edge_arc
    real(8), allocatable, dimension(:  ) :: cell_area
    real(8), allocatable, dimension(:  ) :: dual_cell_area
    real(8), allocatable, dimension(:,:) :: vtx_kite_area  ! The intersection area of dual_cell_area with each cell that radiates from a given vertex
    ! Topology
    integer, allocatable, dimension(:  ) :: ne_of_cell
    integer, allocatable, dimension(:  ) :: ne_of_edge
    integer, allocatable, dimension(:,:) :: cell_of_cell
    integer, allocatable, dimension(:,:) :: cell_of_edge
    integer, allocatable, dimension(:,:) :: cell_of_vtx
    integer, allocatable, dimension(:,:) :: edge_of_cell
    integer, allocatable, dimension(:,:) :: edge_of_edge
    integer, allocatable, dimension(:,:) :: edge_of_vtx
    integer, allocatable, dimension(:,:) :: vtx_of_cell
    integer, allocatable, dimension(:,:) :: vtx_of_edge
  contains
    procedure :: init => scvt_mesh_init
    procedure :: clear => scvt_mesh_clear
    final scvt_final
  end type scvt_mesh_type

  integer, parameter :: i4_missing_value = -1

contains

  subroutine scvt_mesh_init(this, nc, ne, nv)

    class(scvt_mesh_type), intent(inout) :: this
    integer, intent(in) :: nc, ne, nv

    this%nc = nc
    this%ne = ne
    this%nv = nv

    allocate(this%cell_lon      (nc))
    allocate(this%cell_lat      (nc))
    allocate(this%cell_x        (nc))
    allocate(this%cell_y        (nc))
    allocate(this%cell_z        (nc))
    allocate(this%edge_lon      (ne))
    allocate(this%edge_lat      (ne))
    allocate(this%edge_x        (ne))
    allocate(this%edge_y        (ne))
    allocate(this%edge_z        (ne))
    allocate(this%vtx_lon       (nv))
    allocate(this%vtx_lat       (nv))
    allocate(this%vtx_x         (nv))
    allocate(this%vtx_y         (nv))
    allocate(this%vtx_z         (nv))

    allocate(this%edge_arc      (ne))
    allocate(this%edge_arc1     (ne))
    allocate(this%edge_arc2     (ne))
    allocate(this%dual_edge_arc (ne))
    allocate(this%cell_area     (nc))
    allocate(this%dual_cell_area(nv))
    allocate(this%vtx_kite_area (nv,nv))

    allocate(this%ne_of_cell    (nc)); this%ne_of_cell = 0
    allocate(this%ne_of_edge    (ne)); this%ne_of_edge = 0
    allocate(this%cell_of_cell  (6 ,nc)); this%cell_of_cell = i4_missing_value
    allocate(this%cell_of_edge  (2 ,ne)); this%cell_of_edge = i4_missing_value
    allocate(this%cell_of_vtx   (3 ,nv)); this%cell_of_vtx  = i4_missing_value
    allocate(this%edge_of_cell  (6 ,nc)); this%edge_of_cell = i4_missing_value
    allocate(this%edge_of_edge  (12,ne)); this%edge_of_edge = i4_missing_value
    allocate(this%edge_of_vtx   (6 ,nv)); this%edge_of_vtx  = i4_missing_value
    allocate(this%vtx_of_cell   (6 ,nc)); this%vtx_of_cell  = i4_missing_value
    allocate(this%vtx_of_edge   (2 ,ne)); this%vtx_of_edge  = i4_missing_value

  end subroutine scvt_mesh_init

  subroutine scvt_mesh_clear(this)

    class(scvt_mesh_type), intent(inout) :: this

    if (allocated(this%cell_lon      )) deallocate(this%cell_lon      )
    if (allocated(this%cell_lat      )) deallocate(this%cell_lat      )
    if (allocated(this%cell_x        )) deallocate(this%cell_x        )
    if (allocated(this%cell_y        )) deallocate(this%cell_y        )
    if (allocated(this%cell_z        )) deallocate(this%cell_z        )
    if (allocated(this%edge_lon      )) deallocate(this%edge_lon      )
    if (allocated(this%edge_lat      )) deallocate(this%edge_lat      )
    if (allocated(this%edge_x        )) deallocate(this%edge_x        )
    if (allocated(this%edge_y        )) deallocate(this%edge_y        )
    if (allocated(this%edge_z        )) deallocate(this%edge_z        )
    if (allocated(this%vtx_lon       )) deallocate(this%vtx_lon       )
    if (allocated(this%vtx_lat       )) deallocate(this%vtx_lat       )
    if (allocated(this%vtx_x         )) deallocate(this%vtx_x         )
    if (allocated(this%vtx_y         )) deallocate(this%vtx_y         )
    if (allocated(this%vtx_z         )) deallocate(this%vtx_z         )

    if (allocated(this%edge_arc      )) deallocate(this%edge_arc      )
    if (allocated(this%edge_arc1     )) deallocate(this%edge_arc1     )
    if (allocated(this%edge_arc2     )) deallocate(this%edge_arc2     )
    if (allocated(this%dual_edge_arc )) deallocate(this%dual_edge_arc )
    if (allocated(this%cell_area     )) deallocate(this%cell_area     )
    if (allocated(this%dual_cell_area)) deallocate(this%dual_cell_area)
    if (allocated(this%vtx_kite_area )) deallocate(this%vtx_kite_area )

    if (allocated(this%ne_of_cell    )) deallocate(this%ne_of_cell    )
    if (allocated(this%ne_of_edge    )) deallocate(this%ne_of_edge    )
    if (allocated(this%cell_of_cell  )) deallocate(this%cell_of_cell  )
    if (allocated(this%cell_of_edge  )) deallocate(this%cell_of_edge  )
    if (allocated(this%cell_of_vtx   )) deallocate(this%cell_of_vtx   )
    if (allocated(this%edge_of_cell  )) deallocate(this%edge_of_cell  )
    if (allocated(this%edge_of_edge  )) deallocate(this%edge_of_edge  )
    if (allocated(this%edge_of_vtx   )) deallocate(this%edge_of_vtx   )
    if (allocated(this%vtx_of_cell   )) deallocate(this%vtx_of_cell   )
    if (allocated(this%vtx_of_edge   )) deallocate(this%vtx_of_edge   )

  end subroutine scvt_mesh_clear

  subroutine scvt_final(this)

    type(scvt_mesh_type), intent(inout) :: this

    call scvt_mesh_clear(this)

  end subroutine scvt_final

  subroutine scvt_mesh_output(DVTs, DTs, DEs, VVTs, VCs, VEs, tag)

    type(array_type), intent(in) :: DVTs
    type(linked_list_type), intent(in) :: DTs
    type(array_type), intent(in) :: DEs
    type(array_type), intent(in) :: VVTs
    type(array_type), intent(in) :: VCs
    type(array_type), intent(in) :: VEs
    character(*), intent(in), optional :: tag

    integer :: max_ne_of_cell = 0
    integer :: max_ne_of_edge = 0
    integer icell, iedge, ivtx, i, j
    real(8) x(6), y(6), z(6)
    type(delaunay_edge_type), pointer :: DE
    type(delaunay_vertex_type), pointer :: DVT
    type(voronoi_cell_type), pointer :: VC, adjVC
    type(voronoi_edge_type), pointer :: VE, VE1, VE2
    type(voronoi_vertex_type), pointer :: VVT
    type(point_type) xp
    type(scvt_mesh_type) mesh
    character(30) name
    character(50) long_name
    character(10) units
    character(20) dim_names(2)
    character(10) dtype

    call mesh%init(VCs%size, VEs%size, VVTs%size)

    do icell = 1, mesh%nc
      VC => get_VC(VCs%value_at(icell))
      mesh%cell_lon(icell) = VC%center%lon
      mesh%cell_lat(icell) = VC%center%lat
      mesh%cell_x(icell) = VC%center%x
      mesh%cell_y(icell) = VC%center%y
      mesh%cell_z(icell) = VC%center%z
      do i = 1, VC%VVTs%size
        VVT => get_VVT(VC%VVTs%value_at(i))
        mesh%vtx_of_cell(i,icell) = VVT%id
      end do
      do i = 1, VC%VEs%size
        VE => get_VE(VC%VEs%value_at(i))
        mesh%edge_of_cell(i,icell) = VE%id
      end do
      do i = 1, VC%adjVCs%size
        adjVC => get_VC(VC%adjVCs%value_at(i))
        mesh%cell_of_cell(i,icell) = adjVC%id
      end do
      mesh%ne_of_cell(icell) = VC%VEs%size
      max_ne_of_cell = max(max_ne_of_cell, mesh%ne_of_cell(icell))
    end do
    do iedge = 1, mesh%ne
      DE => get_DE(DEs%value_at(iedge))
      VE => get_VE(VEs%value_at(iedge))
      if (intersect(DE%DVT1, DE%DVT2, VE%VVT1, VE%VVT2, xp)) then
        mesh%edge_lon(iedge) = xp%lon
        mesh%edge_lat(iedge) = xp%lat
        mesh%edge_x(iedge) = xp%x
        mesh%edge_y(iedge) = xp%y
        mesh%edge_z(iedge) = xp%z
      else
        call log_error('Delaunay edge and Voronoi edge do not intersect!', __FILE__, __LINE__)
      end if
      mesh%edge_arc(iedge) = calc_arc_length(VE%VVT1, VE%VVT2)
      mesh%edge_arc1(iedge) = calc_arc_length(VE%VVT1, xp)
      mesh%edge_arc2(iedge) = calc_arc_length(VE%VVT2, xp)
      mesh%dual_edge_arc(iedge) = calc_arc_length(DE%DVT1, DE%DVT2)
      mesh%vtx_of_edge(1,iedge) = VE%VVT1%id
      mesh%vtx_of_edge(2,iedge) = VE%VVT2%id
      mesh%cell_of_edge(1,iedge) = VE%VC1%id
      mesh%cell_of_edge(2,iedge) = VE%VC2%id
      mesh%ne_of_edge(iedge) = VE%VC1%VEs%size + VE%VC2%VEs%size - 2
      max_ne_of_edge = max(max_ne_of_edge, mesh%ne_of_edge(iedge))
      i = 1
      do j = 1, VE%VC1%VEs%size
        VE1 => get_VE(VE%VC1%VEs%value_at(j))
        if (.not. associated(VE1, VE)) then
          mesh%edge_of_edge(i,iedge) = VE1%id
          i = i + 1
        end if
      end do
      do j = 1, VE%VC2%VEs%size
        VE2 => get_VE(VE%VC2%VEs%value_at(j))
        if (.not. associated(VE2, VE)) then
          mesh%edge_of_edge(i,iedge) = VE2%id
          i = i + 1
        end if
      end do
    end do
    do ivtx = 1, mesh%nv
      VVT => get_VVT(VVTs%value_at(ivtx))
      mesh%vtx_lon(ivtx) = VVT%lon
      mesh%vtx_lat(ivtx) = VVT%lat
      mesh%vtx_x(ivtx) = VVT%x
      mesh%vtx_y(ivtx) = VVT%y
      mesh%vtx_z(ivtx) = VVT%z
      do i = 1, VVT%VCs%size
        VC => get_VC(VVT%VCs%value_at(i))
        mesh%cell_of_vtx(i,ivtx) = VC%id
        VE => get_VE(VVT%VEs%value_at(i))
        mesh%edge_of_vtx(i,ivtx) = VE%id
      end do
    end do

    do icell = 1, mesh%nc
      VC => get_VC(VCs%value_at(icell))
      do i = 1, VC%VVTs%size
        VVT => get_VVT(VC%VVTs%value_at(i))
        x(i) = VVT%x
        y(i) = VVT%y
        z(i) = VVT%z
      end do
      mesh%cell_area(icell) = calc_area(x(:VC%VVTs%size), y(:VC%VVTs%size), z(:VC%VVTs%size))
    end do

    do ivtx = 1, mesh%nv
      VVT => get_VVT(VVTs%value_at(ivtx))
      do i = 1, VVT%DT%DVTs%size
        DVT => get_DVT(VVT%DT%DVTs%value_at(i))
        x(i) = DVT%x
        y(i) = DVT%y
        z(i) = DVT%z
      end do
      mesh%dual_cell_area(ivtx) = calc_area(x(:VVT%DT%DVTs%size), y(:VVT%DT%DVTs%size), z(:VVT%DT%DVTs%size))
    end do

    do ivtx = 1, mesh%nv
      VVT => get_VVT(VVTs%value_at(ivtx))
      do i = 1, VVT%VEs%size
        VE1 => get_VE(VVT%VEs%value_at(i))
        VE2 => get_VE(VVT%VEs%value_at(merge(1, i + 1, i == VVT%VEs%size)))
        VC => get_VC(VVT%VCs%value_at(i))
        x(1) = mesh%edge_x(VE1%id); y(1) = mesh%edge_y(VE1%id); z(1) = mesh%edge_z(VE1%id)
        x(2) = mesh%cell_x(VC %id); y(2) = mesh%cell_y(VC %id); z(2) = mesh%cell_z(VC %id)
        x(3) = mesh%edge_x(VE2%id); y(3) = mesh%edge_y(VE2%id); z(3) = mesh%edge_z(VE2%id)
        x(4) = VVT%x;               y(4) = VVT%y;               z(4) = VVT%z
        mesh%vtx_kite_area(i,ivtx) = calc_area(x(:4), y(:4), z(:4))
      end do
    end do

    call log_notice('Output mesh file ' // 'scvt_mesh.' // to_str(mesh%nc) // '.nc.')

    call fiona_create_dataset('scvt', file_path='scvt_mesh.' // to_str(mesh%nc) // '.nc')
    call fiona_add_dim('scvt', 'nc'            , size=mesh%nc)
    call fiona_add_dim('scvt', 'ne'            , size=mesh%ne)
    call fiona_add_dim('scvt', 'nv'            , size=mesh%nv)
    call fiona_add_dim('scvt', 'max_ne_of_cell', size=max_ne_of_cell)
    call fiona_add_dim('scvt', 'max_ne_of_edge', size=max_ne_of_edge)
    call fiona_add_dim('scvt', 'two'           , size=2)
    call fiona_add_dim('scvt', 'three'         , size=3)
    call fiona_add_dim('scvt', 'six'           , size=6)
    name         = 'cell_lon'
    long_name    = 'Longitude of cell center'
    units        = 'radian'
    dim_names(1) = 'nc'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'cell_lat'
    long_name    = 'Latitude of cell center'
    units        = 'radian'
    dim_names(1) = 'nc'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'edge_lon'
    long_name    = 'Longitude of edge point'
    units        = 'radian'
    dim_names(1) = 'ne'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'edge_lat'
    long_name    = 'Latitude of edge point'
    units        = 'radian'
    dim_names(1) = 'ne'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'vtx_lon'
    long_name    = 'Longitude of vertex'
    units        = 'radian'
    dim_names(1) = 'nv'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'vtx_lat'
    long_name    = 'Latitude of vertex'
    units        = 'radian'
    dim_names(1) = 'nv'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'ne_of_cell'
    long_name    = 'Number of edges of cell'
    units        = ''
    dim_names(1) = 'nc'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'ne_of_edge'
    long_name    = 'Number of edges of edge'
    units        = ''
    dim_names(1) = 'ne'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'cell_of_cell'
    long_name    = 'Cells of cell'
    units        = ''
    dim_names(1) = 'max_ne_of_cell'; dim_names(2) = 'nc'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'cell_of_edge'
    long_name    = 'Cells of edge'
    units        = ''
    dim_names(1) = 'two'; dim_names(2) = 'ne'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'cell_of_vtx'
    long_name    = 'Cells of vertex'
    units        = ''
    dim_names(1) = 'three'; dim_names(2) = 'nv'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'edge_of_cell'
    long_name    = 'Edges of cell'
    units        = ''
    dim_names(1) = 'max_ne_of_cell'; dim_names(2) = 'nc'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'edge_of_edge'
    long_name    = 'Edges of edge'
    units        = ''
    dim_names(1) = 'max_ne_of_edge'; dim_names(2) = 'ne'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'edge_of_vtx'
    long_name    = 'Edges of vertex'
    units        = ''
    dim_names(1) = 'six'; dim_names(2) = 'nv'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'vtx_of_cell'
    long_name    = 'Vertices of cell'
    units        = ''
    dim_names(1) = 'max_ne_of_cell'; dim_names(2) = 'nc'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'vtx_of_edge'
    long_name    = 'Vertices of edge'
    units        = ''
    dim_names(1) = 'two'; dim_names(2) = 'ne'
    dtype        = 'i4'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype, missing_value=i4_missing_value)
    name         = 'cell_area'
    long_name    = 'Area of cell'
    units        = 'm2'
    dim_names(1) = 'nc'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'dual_cell_area'
    long_name    = 'Area of dual cell'
    units        = 'm2'
    dim_names(1) = 'nv'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'vtx_kite_area'
    long_name    = 'Overlap area between cell and dull cell'
    units        = 'm2'
    dim_names(1) = 'three'; dim_names(2) = 'nv'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:2), dtype=dtype)
    name         = 'edge_arc'
    long_name    = 'Arc length of edge'
    units        = 'm'
    dim_names(1) = 'ne'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'edge_arc1'
    long_name    = 'Arc length between edge point and vertex 1'
    units        = 'm'
    dim_names(1) = 'ne'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)
    name         = 'edge_arc2'
    long_name    = 'Arc length between edge point and vertex 2'
    units        = 'm'
    dim_names(1) = 'ne'
    dtype        = 'r8'
    call fiona_add_var('scvt', name, long_name=long_name, units=units, dim_names=dim_names(1:1), dtype=dtype)

    call fiona_start_output('scvt')
    call fiona_output('scvt', 'cell_lon'      , mesh%cell_lon      )
    call fiona_output('scvt', 'cell_lat'      , mesh%cell_lat      )
    call fiona_output('scvt', 'edge_lon'      , mesh%edge_lon      )
    call fiona_output('scvt', 'edge_lat'      , mesh%edge_lat      )
    call fiona_output('scvt', 'vtx_lon'       , mesh%vtx_lon       )
    call fiona_output('scvt', 'vtx_lat'       , mesh%vtx_lat       )
    call fiona_output('scvt', 'ne_of_cell'    , mesh%ne_of_cell    )
    call fiona_output('scvt', 'ne_of_edge'    , mesh%ne_of_edge    )
    call fiona_output('scvt', 'cell_of_cell'  , mesh%cell_of_cell  )
    call fiona_output('scvt', 'cell_of_edge'  , mesh%cell_of_edge  )
    call fiona_output('scvt', 'cell_of_vtx'   , mesh%cell_of_vtx   )
    call fiona_output('scvt', 'edge_of_cell'  , mesh%edge_of_cell(:max_ne_of_cell,:))
    call fiona_output('scvt', 'edge_of_edge'  , mesh%edge_of_edge(:max_ne_of_edge,:))
    call fiona_output('scvt', 'edge_of_vtx'   , mesh%edge_of_vtx   )
    call fiona_output('scvt', 'vtx_of_cell'   , mesh%vtx_of_cell   )
    call fiona_output('scvt', 'vtx_of_edge'   , mesh%vtx_of_edge   )
    call fiona_output('scvt', 'cell_area'     , mesh%cell_area     )
    call fiona_output('scvt', 'dual_cell_area', mesh%dual_cell_area)
    call fiona_output('scvt', 'vtx_kite_area' , mesh%vtx_kite_area )
    call fiona_output('scvt', 'edge_arc'      , mesh%edge_arc      )
    call fiona_output('scvt', 'edge_arc1'     , mesh%edge_arc1     )
    call fiona_output('scvt', 'edge_arc2'     , mesh%edge_arc2     )
    call fiona_end_output('scvt')

  end subroutine scvt_mesh_output

end module scvt_mesh_mod
